package stone926.mods.more_enchantments.mixins.sacrifice;

import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import stone926.mods.more_enchantments.damage.StoneDamageSource;
import stone926.mods.more_enchantments.enchantments.SacrificeEnchantment;
import stone926.mods.more_enchantments.interfaces.IEntityScheduleDie;

import java.util.Random;

@Mixin(Entity.class)
public abstract class EntityMixinSacrifice implements IEntityScheduleDie {

  @Shadow
  public World world;
  @Shadow
  @Final
  protected Random random;
  private int lvl = -1;
  private int dieTicks = -1;
  private boolean needDie = false;
  private PlayerEntity attacker = null;
  private boolean shouldSacrificeAfterEntityDeath = true;
  private ParticleEffect particle;
  private int count;
  private double deltaX;
  private double deltaY;
  private double deltaZ;
  private double speed;

  @Shadow
  public abstract void kill();

  @Shadow
  public abstract double getX();

  @Shadow
  public abstract double getY();

  @Shadow
  public abstract double getZ();

  @Shadow
  public abstract float getHeight();

  @Shadow
  public abstract boolean damage(DamageSource source, float amount);

  @Override
  public void scheduleToDie(int ticks) {
    if (dieTicks != -1) dieTicks = Math.min(dieTicks, ticks);
    else dieTicks = ticks;
    needDie = true;
    shouldSacrificeAfterEntityDeath = true;
  }

  @Override
  public int getTicksFromDie() {
    return dieTicks;
  }

  @Override
  public void setNeedDie(boolean needDie) {
    this.needDie = needDie;
  }

  @Override
  public boolean needDie() {
    return needDie;
  }

  @Override
  public PlayerEntity getAttacker() {
    return attacker;
  }

  @Override
  public void setAttacker(PlayerEntity player) {
    this.attacker = player;
  }

  @Override
  public int getLvl() {
    return lvl;
  }

  @Override
  public void setLvl(int lvl) {
    this.lvl = lvl;
  }

  @Override
  public <T extends ParticleEffect> void setParticle(T particle, int count, double deltaX, double deltaY, double deltaZ, double speed) {
    this.particle = particle;
    this.count = count;
    this.deltaX = deltaX;
    this.deltaY = deltaY;
    this.deltaZ = deltaZ;
    this.speed = speed;
  }

  @Override
  public <T extends ParticleEffect> void spawnParticleBeforeDeath(T particle, double x, double y, double z, int count, double deltaX, double deltaY, double deltaZ, double speed) {
    if (needDie && !world.isClient && random.nextDouble() <= 0.35) {
      ((ServerWorld) world).spawnParticles(particle, x, y, z, count, deltaX, deltaY, deltaZ, speed);
    }
  }

  @Inject(method = "tick", at = @At("HEAD"))
  private void tick(CallbackInfo ci) {
    if (needDie) {
      if (this.dieTicks == 0) {
        if (attacker == null) {
          this.kill();
        } else {
          if (this.damage(DamageSource.player(attacker), Integer.MAX_VALUE)) {
            if (shouldSacrificeAfterEntityDeath) {
              SacrificeEnchantment.sacrificeAfterEntityDeath(attacker, lvl);
              shouldSacrificeAfterEntityDeath = false;
            }
            SacrificeEnchantment.onEntityDie(world, (Entity) (Object) this, lvl);
          }
        }
        needDie = false;
      } else if (dieTicks > 0) {
        dieTicks--;
        if ((Entity) (Object) this instanceof LivingEntity livingThis) {
          SacrificeEnchantment.boostDyingLivingEntity(livingThis, lvl);
          livingThis.damage(StoneDamageSource.SACRIFICE_ENCHANTMENT, 1);
        }
        if (attacker != null) {
          SacrificeEnchantment.sacrificeDuringTiming(attacker, lvl);
        }
        spawnParticleBeforeDeath(particle, this.getX(), this.getY() + this.getHeight() + 1.5, this.getZ(), count, deltaX, deltaY, deltaZ, speed);
      }
    }
  }

}
